[TLE] barrier & wgmma pipeline with user promising support in hopper - #707
Conversation
|
Please check which newly code should be inside |
b475f86 to
abc69cd
Compare
sunnycase
left a comment
There was a problem hiding this comment.
Thanks for the work on the Hopper pipeline support. One concern I have is that this introduces a separate async_task/async_tasks API for warp specialization while tle/gluon already has an existing warp_specialize API.
Could we please reuse or extend the existing warp_specialize API here instead? Having another async-task abstraction for the same concept would split the user-facing API surface and make it unclear which API users should choose. Aligning this with the existing API would keep the programming model more consistent and easier to understand.
fixed in commit a3e7dfb. I preserve the Two notes:
|
|
Thanks for the update. Since the existing Keeping two APIs for the same functionality would make it unclear which one users should choose, and it may give the impression that TLE does not have a unified programming model or API design. I think we should keep this centered on If there is still something that cannot be expressed through |
If there is API like |
|
I do not think we need to add a separate So please migrate away from |
got it, I will remove the accessor, and replace it with function args. |

FlagTree 前端支持
通过使用tle.barrier,tle.wgmma,tle.wgmma_wait等新增API,并基于已有
tle.gpu.warp_specializeAPI在FlagTree Triton中实现Hopper架构上的自定义复杂流水排布。具体样例参考commit tle_hopper_fa_ws_pipelined_pingpong_persistent.py实现hopper版sdpa样例。API修改总览:
新增 barrier API:
tle.gpu.alloc_barriers(...)tle.gpu.alloc_barrier(...)tle.gpu.barrier_wait(bar, phaseIdx=None)tle.gpu.barrier_arrive(bar, arrive_count=1, phaseIdx=None)tle.gpu.PENDINGtle.gpu.READYtle.gpu.barriertle.gpu.barrier_type新增 WGMMA API:
tle.gpu.wgmma(...)tle.gpu.wgmma_wait(pendings, acc=None)已有 API 扩展:
tle.gpu.copy(...)新增barrier参数,用于 TMA global-to-shared load 的显式 completion barrier。Warp Specialization使用说明
使用
tle.gpu.warp_specializeAPI 时,需要把 producer / consumer partition 拆成独立的@triton.jit函数,并在 kernel 内用显式列表声明调用关系。API 形式
参数:
functions_and_args:partition 列表。每一项必须是(fn, args),其中fn是@triton.jit函数,args必须是 tuple。functions_and_args[0]:default partition,通常作为 producer。它使用 kernel launch 的num_warps。functions_and_args[1:]:worker partitions,通常作为 consumer / MMA partitions。worker_num_warps:每个 worker partition 的 warp 数,长度必须等于len(functions_and_args) - 1。worker_num_regs:每个 worker partition 的 requested registers,长度必须等于len(functions_and_args) - 1。使用约定:
functions_and_args[0]是 default partition,通常放 producer body。functions_and_args[1:]是 worker partitions,通常放 consumer / MMA body;如果需要多个 consumer,就重复添加多个 worker entry。多个 consumer 需要区分行为时,可以显式传入 constexpr 参数,例如
CID。worker function 的参数可以捕获 shared-memory buffer、barrier、descriptor、标量 constexpr 等。多个 worker 捕获同一个值时,前端会去重并作为
ttg.warp_specialize的 partition operand 传入。使用以下 API 会启用 TLE user-promise pipeline mode:
tle.gpu.alloc_barrierstle.gpu.alloc_barriertle.gpu.wgmma_wait该 mode 下:
mlir::triton::gpu::detail::scheduleTleWgmmaUserPromisePipeline处理 WGMMA user-promise pipeline,并为 WGMMA 补齐ttng::WarpGroupDotCommitOp。tle.gpu.warp_specialize调用前已经产生,则当前warp_specialize会 inline 展开functions_and_args;否则保持tt.call调用形式。如果需要在 worker partition 中显式使用 barrier,应在
tle.gpu.warp_specialize前分配 barrier,并把它作为functions_and_args的参数传给 producer / consumer。该样例表示重复添加两个
consumerentry,让同一段 consumer body 生成两个 worker partition。每个 worker 都使用num_warps=4和requested registers=168。用户可以在函数参数中传入 constexpr 值如
0和1作为CID区分两个 consumer worker。上面的empty是 mbarrier 示例;如果要做 consumer warpgroup 之间的 ping-pong 同步,参考后面的 named barrier 示例。Barrier 相关 API
API 参数简介
本节只说明参数含义。
arrive_count和phaseIdx的具体配置方式见后面的使用示例。tle.gpu.alloc_barriers(...)参数:
num_barriers:tle.gpu.alloc_barriers返回一个barrier数组,此处为分配的 barrier slot 数量,必须是编译期正整数。arrive_count:barrier 的计数参数,即 pending arrival count。必须是编译期正整数。mbarrier 和 named barrier 后端对它的解释不同。具体参考使用示例。init:barrier初始状态,支持tle.gpu.PENDING和tle.gpu.READY。expect_bytes:TMA completion barrier 期望完成的字节数。为None或编译期正整数。返回值:
tle.gpu.barrier,表示 barrier 数组或 barrier slot。数组通过bars[i]取 slot。tle.gpu.alloc_barrier(...)返回值:单个
tle.gpu.barrierslot。它是alloc_barriers(num_barriers=1, ...)的语法糖。tle.gpu.barrier_wait(...)参数:
bar:要等待的 barrier slot,类型为tle.gpu.barrier。phaseIdx:mbarrier logical use id。传入时使用 mbarrier 路径;为None时使用 named barrier 路径。tle.gpu.barrier_arrive(...)参数:
bar:要 arrive 的 barrier slot,类型为tle.gpu.barrier。arrive_count:本次 arrive 的计数权重。arrive_count表达。phaseIdx:mbarrier logical use id,第几次使用这个 slot。传入时使用 mbarrier 路径;为None时使用 named barrier 路径。tle.gpu.PENDING含义:barrier 初始不可通过。常用于 full barrier、TMA completion barrier、named barrier。
tle.gpu.READY含义:barrier 初始可通过。常用于 empty barrier。
READYbarrier 只能走 mbarrier 路径。tle.gpu.barrier含义:
alloc_barrier(s)返回的 barrier value 类型。支持bars[i]取数组 slot。tle.gpu.barrier_type含义:
tle.gpu.barrier对应的类型描述。普通 kernel 代码通常不需要直接构造。修改
tle.gpu.copy(..., barrier=...)参数:
src:copy 源。使用barrier时必须是 TMA TensorDescriptor。dst:copy 目标。使用barrier时必须是 shared-memorytle.gpu.buffered_tensor。shape:copy tile 的形状。offsets:TMA descriptor 坐标偏移。barrier:可选 completion barrier。必须来自tle.gpu.alloc_barrier(s)(expect_bytes=...)。使用示例
mbarrier 使用路径
传入
phaseIdx时,barrier_wait/barrier_arrive会选择 mbarrier 路径。tle.gpu.copy(..., barrier=...)的 TMA completion barrier 也必须走 mbarrier 路径,并且后续 wait 必须传phaseIdx。适用场景:
init=tle.gpu.READY的 empty barrier。phaseIdx要求:phaseIdx是同一个 barrier slot 的 logical use id,也就是这个 slot 第几次被使用。它的设计意图是让循环里的同一组 barrier slot 可以被反复复用,并通过循环计数区分第几轮复用。(phaseIdx & 1) ^ init_polarityphaseIdx & 1,再根据init调整初始 polarity& 1。phaseIdx。slot = iter % NUM_BUFFERS,phaseIdx = iter // NUM_BUFFERS,因为同一个 slot 每隔NUM_BUFFERS次迭代才复用一次。arrive_count配置示例:一个 consumer 到达同一个 slot 一次:
两个 worker consumers 共同释放同一个 slot,此时同一个 slot 会收到两次 arrive,所以
arrive_count=2:两个 worker consumers 使用各自独立 slot:传入barrier slots array,通过CID区分自己需要使用的slot。
其他注意事项:
后端选择:
barrier_wait/barrier_arrive只要传入phaseIdx,当前 slot 就会记录为 mbarrier backend。初始状态:
init=tle.gpu.READY只适合 mbarrier 路径。READYbarrier 不传phaseIdx,会选择 named barrier 路径并报错。TMA completion:
expect_bytesbarrier 只能用于 TMA global-to-shared completion。expect_bytes的 barrier 必须通过tle.gpu.copy(..., barrier=...)触发 completion,并且必须用phaseIdx等待。named barrier 使用路径
不传
phaseIdx时,barrier_wait/barrier_arrive会选择 named barrier 路径。适用场景:
init=READY。named_barrier的arrive_count是参与线程数named_num_threads,通常写成参与 warp 数 * 32。当前实现会把 allocation 的arrive_count同时传给 named arrive 和 named wait。因此,使用named_barrier只需要alloc后直接wait+arrive。举例:两个 4-warp consumer workers 通过同一个 named barrier 同步,总参与线程数是 256。这里两个 worker 复用同一段 consumer body,并通过显式传入的
CID区分一个 worker 做 non-blocking arrive,另一个 worker 做 wait/sync;不需要在barrier_arrive/barrier_wait调用参数里写arrive_count。其他注意事项:
named barrier只支持init=tle.gpu.PENDING,不支持READY。named barrier不支持expect_bytes,因此不能用于tle.gpu.copy(..., barrier=...)的 TMA completion。named barrier要求静态 barrier slot index,例如sync[0];不能用动态 slot index。Empty-Full 同步示例
copy(..., barrier=...)只支持 TMA global-to-shared load。不支持 shared-to-global TMA store、普通 copy、named barrier 或没有expect_bytes的 barrier。TMA completion 示例
TMA completion 只能使用 mbarrier 后端,通常写
arrive_count=1,因为 arrival 来自这一条 TMA copy 的 completion:WGMMA 相关 API
API 参数简介
tle.gpu.wgmma(...)参数:
a:WGMMA A operand。可以是 shared-memorytle.gpu.buffered_tensor或 registertl.tensor。b:WGMMA B operand。当前必须是 shared-memorytle.gpu.buffered_tensor。acc:输入 accumulator。为None时前端会生成零 accumulator。input_precision:dot input precision。为None时使用当前 builder 默认值。max_num_imprecise_acc:FP8 imprecise accumulation 控制参数,不改变返回 tensor dtype,仅在FP8 GEMM上有效。使用 FP8 -> F32 WGMMA 当前有效值需 >= 32;None 在 sm90 上默认 2^30。值越小越偏精度,越大越偏性能。out_dtype:输出 accumulator dtype,默认tl.float32。它决定结果/accumulator 的 dtype;trans_a:是否转置 A operand,必须是编译期 bool。trans_b:是否转置 B operand,必须是编译期 bool。返回值:异步 WGMMA accumulator dependency value。普通 tensor op、reduce、store 消费前必须调用
tle.gpu.wgmma_wait(...)。tle.gpu.wgmma_wait(...)参数:
pendings:允许保留的 outstanding WGMMA group 数量。0表示完全等待。acc:要等待的 accumulator。若省略且第一个参数是 tensor,则等价于tle.gpu.wgmma_wait(0, acc)。返回值:wait 后可安全消费的 accumulator tensor。
使用示例
单次 WGMMA:
流水中保留一个 pending group,WGMMA是warpgroup级别的FIFO队列模型:
当前性能
测试说明
vllm.vllm_flash_attn.flash_attn_interface.flash_attn_varlen_functorch.nn.attention.varlen.varlen_attn通过FlagGems benchmark测试,右侧三列表示以vllm为100%性能,其他三个的性能比。